<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/2/18
 * Time: 22:00
 */

declare(strict_types=1);

namespace App\Service;
use App\Model\Dao\CommissionSalesTransDao;
use App\Model\Dao\MchAccountAccessDao;
use App\Model\Dao\MchAccountAccessDetailDao;
use App\Model\Dao\MchAccountDao;
use App\Utils\CustomUtils;
use Exception;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;

/**
 * Class SettleCommissionWithdrawService
 * @package App\Service
 * 佣金提现结算
 */
class SettleCommissionWithdrawService extends CommonService
{

    /**
     * @Inject()
     * @var CommissionSalesTransDao
     */
    private $CommissionSalesTransDao;

    /**
     * @Inject()
     * @var MchAccountDao
     */
    private $MchAccountDao;

    /**
     * @Inject()
     * @var MchAccountAccessDao
     */
    private $MchAccountAccessDao;

    /**
     * @Inject()
     * @var MchAccountAccessDetailDao
     */
    private $MchAccountAccessDetailDao;

    public function handle($param) {
        // 校验
        $trans = $this->CommissionSalesTransDao->get($param['commissionTransId']);
        if(!$trans) {
            throw new Exception('佣金提现订单不存在，终止处理', FAILED_CODE);
        }
        if($trans['settleStatus'] == 1) {
            throw new Exception('佣金提现订单已经结算', FAILED_CODE);
        }

        // 除了复审通过，其他都不操作划账，仅更新结算状态
        if ($trans['handleStatus'] != 3) {
            $updateTradeOrder = array(
                'settleStatus'  => 1,
            );
            $this->CommissionSalesTransDao->update($param['commissionTransId'], $updateTradeOrder);
            return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "佣金提现订单结算成功");
        }

        // 转到银行卡的，也不需要划账
        if ($trans['transType'] == 1) {
            return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "佣金提现订单结算成功");
        }

        $isAccess = $this->MchAccountAccessDao->getWhere(['commissionTransId' => $param['commissionTransId'], 'accessType' => ACCESS_TYPE_COMMISSION_WITHDRAW]);
        if ($isAccess) {
            throw new Exception('佣金提现订单已进行结算', FAILED_CODE);
        }
        $mchAccount = $this->MchAccountDao->get($param['mchAccountId']);
        if(!$mchAccount) {
            throw new Exception('查询账户记录失败', FAILED_CODE);
        }

        // 乐观锁版本号+1
        $updateVersion = $mchAccount['updateVersion'] + 1;

        // 结算金额 余额前后
        $transAmount = $trans['transAmount'];
        $beforeTradeCashAmount = $mchAccount['tradeCashAmount'];
        $afterTradeCashAmount = $mchAccount['tradeCashAmount'] + $transAmount;

        // 开启事务
        Db::beginTransaction();
        try {
            // step
            // 1.结算金额入账可提现账户
            // 2.更新订单状态
            // 3.更新账户余额

            // step 1
            $this->handleInCommissionTransAmount($param, $trans, $mchAccount, $transAmount, $beforeTradeCashAmount, $afterTradeCashAmount);

            // step 2
            $updateTradeOrder = array(
                'settleStatus'  => 1,
                'settleTime'    => time()
            );
            $this->CommissionSalesTransDao->update($param['commissionTransId'], $updateTradeOrder);

            // step 3
            $updateMchAccount = array(
                "tradeCashAmount"   => $afterTradeCashAmount,
                "updateVersion"     => $updateVersion
            );
            $result = $this->MchAccountDao->updateWhere([
                "id" => $param['mchAccountId'],
                "updateVersion" => $mchAccount['updateVersion']
            ], $updateMchAccount);

            // 如果更新账户失败，重试，最多3次
            if (!$result) {
                throw new \Exception("更新账户失败");
            }

        } catch (\Throwable $e) {
            CustomUtils::xzxDebug($e->getMessage());
            Db::rollBack();

            // 失败重试，最多3次
            $this->tryOneMore($param);

            return CustomUtils::xeServiceReturnData(FAILED_CODE, "佣金提现订单结算失败");
        }
        Db::commit();

        return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "佣金提现订单结算成功");

    }

    /**
     * @param $param
     * @param $trans
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * 交易可提现账户入账
     */
    private function handleInCommissionTransAmount($param, $trans, $mchAccount, $amount, $beforeAmount, $afterAmount) {

        // 写主表
        $mchAccountAccess = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $mchAccount["mchId"],
            "mchNo"             => $mchAccount["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accessType"        => TYPE_COMMISSION_WITHDRAW_CASH_IN,
            "accountType"       => ACCOUNT_TYPE_CASH,
            "commissionTransId" => $trans['id'],
            "commissionTransNo" => $trans['transNo'],
            "amount"            => $amount,
            "currency"          => $trans['transCurrency'],
            "status"            => 1,
            "remark"            => "佣金提现单入账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDao->add($mchAccountAccess);

        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $mchAccount["mchId"],
            "mchNo"             => $mchAccount["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accessId"          => $mchAccountAccess['id'],
            "accountType"       => ACCOUNT_TYPE_CASH,
            "inOutType"         => DETAIL_TYPE_IN,
            "type"              => TYPE_COMMISSION_WITHDRAW_CASH_IN,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => $amount,
            "commissionTransId" => $trans['id'],
            "commissionTransNo" => $trans['transNo'],
            "currency"          => $trans['transCurrency'],
            "remark"            => "佣金提现单入账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
    }

}